// 1020. [拓扑排序有向无环图]任务调度算法
// https://oj.rnd.huawei.com/problems/1020/details 输入 第一行为任务的数量
// taskNum ，其值范围为：[1, 1000] 第二行为依赖关系的数量 relationsNum
// ，其值范围：[0, 500000] 接下来 relationsNum 行，每行描述一个依赖关系，格式为
// IDi>IDj， 表示任务 i 依赖任务 j ，IDi 和 IDj 值的范围为：[1, taskNum] 输出
// 一个整数，代表执行完所有任务的最短时间。 有向无环图DAG,拓扑,BFS,层序遍历 9
// 个 task [1-9] 6 个关联 1>2 表示R[2]=1,先2后1,即2->1,即1的入度+1 2>3 2>4 4>5
// 6>4
// 8>7
// 输出 4

#include <algorithm>
#include <iostream>
#include <queue>
#include <utility>
#include <vector>
using namespace std;

// 执行完所有任务的最短时间
int GetMinTime(int taskNum, const vector<pair<int, int>> &relations) {
  // adjRelation构造每个任务的邻接表, Rela[B]=A 表示B之后有A
  vector<vector<int>> adjacentRelation(taskNum, vector<int>());
  // 每个任务的入度表,初始0
  vector<int> inDegree(taskNum, 0);
  for (auto re : relations) {
    adjacentRelation[re.second - 1].push_back(re.first - 1);
    inDegree[re.first - 1]++;
  }
  // 0入度先进队列
  queue<int> quTask;
  for (int i = 0; i < taskNum; ++i) {
    if (inDegree[i] == 0) {
      quTask.push(i);
    }
  }
  int cnt = 0;
  while (!quTask.empty()) {
    int nProCount = quTask.size();
    cnt++;  // 出一波队列算一次
    for (int i = 0; i < nProCount; i++) {
      int node = quTask.front();
      quTask.pop();
      // 入度减一
      for (int task : adjacentRelation[node]) {
        inDegree[task]--;
        if (inDegree[task] == 0) {
          quTask.push(task);  // 进
        }
      }
    }
  }
  return cnt;
}

// =========
int main() {
  int taskNum;
  cin >> taskNum;
  int relationNum;
  cin >> relationNum;
  vector<pair<int, int>> relations(relationNum);
  for (int i = 0; i < relationNum; ++i) {
    char delimiter;
    cin >> relations[i].first >> delimiter >> relations[i].second;
  }
  cout << GetMinTime(taskNum, relations);
  return 0;
}
